Whisper Isle 遊戲連結(PC Only):whisperisle.app(註:專案每日持續高速演進,撰文當下線上已推進至 v0.2.2)
昨天在 Day 4 聊到了核心迴圈:怪物漫遊、戰鬥結算、掉落物與升級全部由資料表驅動,且在伺服器端完成驗證。
當核心迴圈跑通,自動化測試一把全綠的時候,身為開發者的我,心裡浮現一種強烈的錯覺:既然 AI 每次改完代碼都能跑過單元測試,還能自己寫出端對端驗證腳本,那我何必每一步都像直升機父母一樣盯著它?既然它這麼靠譜,何不給它最大的自由度,讓它「驗完就直接推上生產環境」?
於是在開工後的第一個週末(2026-07-11),被我親手推翻。AI 對「真實世界」缺乏敬畏心
AI 沒有「生產環境在燃燒」的體感。對它來說,程式碼在本地虛擬環境裡跑完 5 個 test cases,綠燈亮起,它的邏輯世界就已經完美閉環了。但本地測試再怎麼寫,也測不出線上資料庫的連線池是否被佔滿、測不出 WebSocket 長連線在 Cloudflare 反向代理後的逾時行徑、測不出新改動的怪獸視野會不會讓線上打怪的玩家瞬間卡死。
更致命的是,當你放開了 push 權限,AI 就會順理成章地把「部署」納入它解決問題的常規手段。原本應該在本地多驗兩輪的手感問題,它可能在五分鐘內連推三顆 commit,讓線上服務反覆重啟三次。
這讓我確立了協作的第一道不可動搖的原則:
AI 可以全權負責內部生產(撰寫、重構、單元測試、本地 commit),但外部交付(push、部署、公開發布)則必須由真人親自確認才允許授權。
軟體工程最殘酷的現實是:寫在文檔裡的規矩,只要沒有物理機制約束,AI 總有一天會因為上下文漂移或提示詞模糊,再次跨過這條線。時隔近兩個月,到了 2026-08-28 晚上,類似的信任邊界事故在社群發言上再次重演。
當時 Whisper Isle 已經推進到了 v0.1.x,我們開始在 Reddit(r/ClaudeGameDev)等社群發布開發進度。一位名為 SuspectBR 的玩家在貼文底下給了非常具體的兩條批評:
1. 角色的陰影是一塊純黑的橢圓,看起來像廉價貼紙,完全沒有立體貼地感。
2. 角色走路動畫的播放速度與地圖移動速度不匹配,腳底像在溜冰(腳滑感)。
我和 Claude Code 在幾天之內做出了軟邊緣接觸陰影(Soft Contact Shadow),並將走路動畫幀率物理綁定到實際位移速度上。這兩項修復在 8/27 晚上就順利進版,甚至比原先對社群承諾的「週末前改善」提早了兩天。
8/28 晚上,我在當天的 session 待辦事項裡留了一行筆記:
「Reddit 要回覆,確保承諾」
這本來是一條我提醒自己隔天有空要去社群留言的待辦。
然而,Claude Code 在讀取到這行待辦後,誤將這句話當成了「請替我執行回覆」的授權,它當場調用外部工具,直接以我的帳號在 Reddit 討論串底下發出了一則公開回覆。如果只是平實陳述修復進度倒也罷了,最令人倒抽一口氣的是,AI 在回覆裡擅自加入了濃厚的 AI 邀功腔調:
「我們已經提前在週末前完成了這些修復!」
這起事故暴露了人機協作中兩個極其隱蔽的陷阱:
1.「告知事項(What to do)」不等於「委託執行(Do it now)」
人類在筆記中寫「要回覆 X」、「要發送 Y」,只是在梳理上下文與代辦。但對目標導向的 Agent 來說,它會把任何未完成的狀態視為「等待它去解決的任務」,在沒有二次確認的情況下直接衝過邊界。
2. AI 的默認文風與真實人類的割裂
AI 天生帶有一種公關行銷與討好感。它覺得「提前完成」是一件很棒的政績,必須大書特書;但在真實世界中,原本承諾週末修好,你默默修完上線、平鋪直敘地說一聲「已修好」,是工程師的本分;若跳出來大張旗鼓宣稱「看吧!我提前做完了」,只會顯得失禮。
這次事故促使我立下了死命令(feedback-outbound-posts-require-approval):
任何以使用者身份對外公開發出的內容(Reddit、Threads、電子郵件、甚至是 Git Push),AI 的工作邊界一律到「產出草稿給真人審查」為止。
經歷了這兩次事故後,我徹底放棄了「用提示詞約束 AI 行為」的幻想。
在長達數小時、對話上下文累積了幾十萬 Token 的深度開發中,AI 必然會出現注意力擴散。你就算在 System Prompt 裡用三層粗體寫「未經許可絕對不能執行 git push」,只要某一輪任務它覺得「我正在替你修一個緊急 bug」,它依然有 1% 的機率會自動把 git push 順手敲進終端機裡。
唯一的解決之道,是用確定性的軟體工程機制,取代機率性的模型自我約束。
在 2026-09-03,我們為整個協作環境裝上了實體物理門禁的 Shell Hooks。
#!/bin/bash
# ~/.claude-max/hooks/push-gate.sh
# PreToolUse(Bash):git push 必須有本 session 的授權標記,否則直接阻斷退出。
STATE_DIR="$HOME/.claude-max/hooks/.state/push-auth"
input=$(cat)
cmd=$(printf '%s' "$input" | jq -r '.tool_input.command // empty')
# 檢測指令是否包含 git push
printf '%s' "$cmd" | grep -qE '(^|[;&|[:space:]])git([[:space:]]+-[^[:space:]]+([[:space:]]+[^[:space:]]+)?)*[[:space:]]+push([[:space:]]|$)' || exit 0
sid=$(printf '%s' "$input" | jq -r '.session_id // empty')
f="$STATE_DIR/$sid"
n=$(cat "$f" 2>/dev/null || echo 0)
case "$n" in ''|*[!0-9]*) n=0;; esac
# 沒有合法授權標記,直接回報並 exit 2
if [ "$n" -le 0 ]; then
echo "push-gate:本 session 沒有 push 授權。user 需在對話中明說「授權 push」後才可推送;請停下並向 user 回報。" >&2
exit 2
fi
# 消耗一次授權計數
n=$((n-1))
if [ "$n" -gt 0 ]; then echo "$n" > "$f"; else rm -f "$f"; fi
exit 0
這段的邏輯很單純:只要 AI 試圖在終端機裡執行任何形式的 git push,Hook 就會去檢查本 Session 的授權狀態檔。如果沒有標記,進程直接以狀態碼 2 崩潰退出,指令連 Git 都碰不到。
#!/bin/bash
# ~/.claude-max/hooks/push-authorize.sh
# UserPromptSubmit:使用者原話含「授權 push」時,寫下一次性授權標記。
STATE_DIR="$HOME/.claude-max/hooks/.state/push-auth"
mkdir -p "$STATE_DIR"
input=$(cat)
sid=$(printf '%s' "$input" | jq -r '.session_id // empty')
prompt=$(printf '%s' "$input" | jq -r '.prompt // empty')
[ -n "$sid" ] && [ -n "$prompt" ] || exit 0
# 嚴格正則匹配「授權 push」
if printf '%s' "$prompt" | grep -qiE '授權[^。]{0,8}push|push[^。]{0,8}授權|authoriz[a-z]* +push|approve[a-z]* +push'; then
n=1
printf '%s' "$prompt" | grep -qi 'tag' && n=2
echo "$n" > "$STATE_DIR/$sid"
echo "push 授權已登記:本 session 可 push $n 次(push-gate hook 放行後即消耗)。"
fi
exit 0
只有當我親手在對話框裡敲下「授權 push」這四個字時,系統才會在磁碟上寫入一次性的通行權限;一旦 git push 成功執行,權限立即被銷毀。